/**
 * JAVACC DEMO 1.0
 */
package com.apache.rpc.common;

import com.apache.api.api.ApiService;
import com.apache.api.vo.ParamsVo;
import com.apache.api.vo.ResultEntity;
import com.apache.cache.util.Validator;
import com.apache.database.constant.SpringContextLoader;
import com.apache.exception.BusinessException;
import com.apache.exception.ServiceException;
import com.apache.rpc.ProtocolService;
import com.apache.rpc.client.*;
import com.apache.rpc.entity.InterfaceRegister;
import com.apache.rpc.entity.Protocol;
import com.apache.tools.ConfigUtil;
import com.apache.tools.DateUtils;
import com.apache.tools.StrUtil;
import org.apache.log4j.Logger;

import java.util.*;

/**
 * description:  接口调用方式转换类
 *
 * @author Hou Dayu 创建时间：2016-11-14
 */
public class LoadRpcService {

    private static LoadRpcService instance;

    private Logger log = Logger.getLogger(LoadRpcService.class);

    private AbstractProtocolService socketService;

    private AbstractProtocolService httpService;

    private ProtocolService pService;

    private LoadRpcService() {
        socketService = new SocketProtocolService();
        httpService = new HttpProtocolService();
        pService = ProtocolAdapter.getAdapter();
    }

    public static LoadRpcService service() {
        if (null == instance) {
            instance = new LoadRpcService();
        }
        return instance;
    }

    /**
     * description:  service调用方法
     *
     * @param beanId 调用实现的BeanId名称
     * @param methodName 方法名或对应的KEY名
     * @param respType 返回值类型（xml/json）
     * @param params 方法所需要的参数
     */
    @SuppressWarnings("rawtypes")
    public ResultEntity doService(String beanId, String methodName, String respType,
            Map<String, Object> params, Class attrClass) {
        ResultEntity resultEntity = null;
        if (StrUtil.isNull(beanId) || StrUtil.isNull(methodName)) {
            resultEntity = getResultEntity("BeanId及方法代号为空");
            return resultEntity;
        }
        respType = StrUtil.doNull(respType, "xml");
        Protocol protocol = new Protocol();
        protocol.setBeanId(beanId);
        protocol.setRespType(respType);
        protocol.setReqFlag(methodName);
        if (params != null && params.size() > 0) {
            protocol.setParams(params);
        }
        //doMethodParams(protocol, params);
        try {
            String result = "";
            log.info("-");
            log.info("beanId[" + beanId + "],请求参数:" + protocol.toString());
            Object obj = doService(protocol);
            if (RpcUtil.isEmpty(obj)) {
                resultEntity = getResultEntity("接口调用超时");
                return resultEntity;
            }
            if (obj instanceof ResultEntity) {
                return (ResultEntity) obj;
            } else {
                result = String.valueOf(obj);
                if (StrUtil.isNull(result)) {
                    resultEntity = getResultEntity("接口调用超时");
                    return resultEntity;
                }
                if ("json".equalsIgnoreCase(respType)) {
                    resultEntity = jsonToBean(result, ResultEntity.class, attrClass);
                } else {
                    resultEntity = xmlToBean(result, ResultEntity.class);
                }
            }
            params = protocol.getParams();
        } catch (ServiceException e) {
            log.error("执行远程调用接口出错", e);
            resultEntity = getResultEntity("执行远程调用接口出错:" + e.getMessage());
            log.error("请求参数:" + protocol.toString());
        } catch (BusinessException e1) {
            resultEntity = getResultEntity(e1.getMessage());
            log.error("请求参数:" + protocol.toString());
        }
        return resultEntity;
    }

    /**
     * description:  service调用方法
     *
     * @param beanId 调用实现的BeanId名称
     * @param methodName 方法名或对应的KEY名
     * @param params 方法所需要的参数
     */
    @SuppressWarnings("rawtypes")
    public ResultEntity doServiceClient(String beanId, String methodName,
            Map<String, Object> params, InterfaceRegister register) {
        String startTime = DateUtils.Now.fmt_yyyyMMdd_HHmmssSSS();
        ResultEntity resultEntity = null;
        if (StrUtil.isNull(beanId) || StrUtil.isNull(methodName)) {
            resultEntity = getResultEntity("BeanId及方法代号为空");
            return resultEntity;
        }
        String serviceIp = ConfigUtil.getInstance().getValueByKey("registry_protocol");
        if("wbdCacheService".equalsIgnoreCase(beanId) || Validator.isNotNull(register.getReserve1())){//竞价服务不分流
            if ("socket".equalsIgnoreCase(register.getCallType())) {
                socketService.setRegister(register);
                resultEntity = socketService.doService(beanId, methodName, params);
            } else {
                httpService.setRegister(register);
                resultEntity = httpService.doService(beanId, methodName, params);
            }
        }else{ //不是竞价服务，且存在这个beanId
            if (Validator.isNotNull(serviceIp) && Validator.isNull(register.getAddress())) {
                return dubboServiceClient(beanId, methodName, params);
            }
            if (pService.isExites(beanId)) {
                resultEntity = doService(beanId, methodName, "xml", params, null);
            } else {
                if ("socket".equalsIgnoreCase(register.getCallType())) {
                    socketService.setRegister(register);
                    resultEntity = socketService.doService(beanId, methodName, params);
                } else {
                    httpService.setRegister(register);
                    resultEntity = httpService.doService(beanId, methodName, params);
                }
            }
        }
        if (Validator.isNotNull(serviceIp) && "T"
                .equalsIgnoreCase(ConfigUtil.getInstance().getValueByKey("rpcSaveLog"))) {
            String stopTime = DateUtils.Now.fmt_yyyyMMdd_HHmmssSSS();
            String resources = getSources();
            Map map = new HashMap();
            map.putAll(params);
            map.put("startTime", startTime);
            map.put("stopTime", stopTime);
            map.put("beanId", beanId);
            map.put("methodKey", methodName);
            map.put("requestSources",
                    resources + StrUtil.doNull(String.valueOf(map.get("rpcExeTimeAddr")), ""));
            SaveRunableRpcLogHelper.getInstance().exeMonitoring(map);
        }
        return resultEntity;
    }

    public ResultEntity dubboService(String beanId, String methodKey, Map<String, Object> params) {
        ApiService service = (ApiService) SpringContextLoader.getBean("rpcService");
        if (null == service) {
            return doService(beanId, methodKey, "xml", params, null);
        }
        params.put("MethodCode", methodKey);
        params.put("BeanId", beanId);
        String resources = getSources();
        params.put("requestSources", resources);
        ParamsVo vo = new ParamsVo();
        vo.setParams(params);
        vo.setMethodKey("dubboService");
        ResultEntity entity = service.doService(vo);
        return entity;
    }

    public ResultEntity dubboServiceClient(String beanId, String methodKey,
            Map<String, Object> params) {
        String serviceIp = ConfigUtil.getInstance().getValueByKey("registry_protocol");
        if (StrUtil.isNull(serviceIp)) {
            ResultEntity entity = new ResultEntity();
            entity.setResult("false");
            entity.setMessage("请注册接口服务地址");
            return entity;
        }
        String resources = getSources();
        String[] str = serviceIp.split(",");
        String address = str[0];
        long dd = RpcUtil.getInstance().getCountMap(address);
        for (int i = 1; i < str.length; i++) {
            if (RpcUtil.getInstance().getCountMap(str[i]) < dd) {
                dd = RpcUtil.getInstance().getCountMap(str[i]);
                address = str[i];
            }
        }
        InterfaceRegister register = new InterfaceRegister();
        ResultEntity entity = null;
        if (!"rpcService".equalsIgnoreCase(beanId)) {
            params.put("MethodCode", methodKey);
            params.put("BeanId", beanId);
        }
        String method = "rpcService".equalsIgnoreCase(beanId) ? methodKey : "dubboService";
        params.put("requestSources", resources + address);
        if (address.startsWith("http:")) {
            register.setCallType("http");
            register.setAddress(address);
            httpService.setRegister(register);
            entity = httpService.doService("rpcService", method, params);
        } else {
            String[] regparam = address.split(":");
            register.setAddress(regparam[0]);
            register.setPort(regparam[1]);
            register.setCallType("socket");
            socketService.setRegister(register);
            entity = socketService.doService("rpcService", method, params);
        }
        if (null == entity.getEntity()) {
            entity = getResultEntity("执行远程调用接口失败");
        } else {
            if (entity.getEntity() instanceof ResultEntity) {
                entity = (ResultEntity) entity.getEntity();
            } else {
                String result = String.valueOf(entity.getEntity());
                if (StrUtil.isNull(result)) {
                    entity = getResultEntity("接口调用超时");
                } else {
                    entity = xmlToBean(result, ResultEntity.class);
                }
            }
        }
        RpcUtil.getInstance().delCountMap(address);
        return entity;
    }

    public <T> T xmlToBean(String xmlStr, Class<T> cls) {
        return JsonEntityParese.instance().toBean(xmlStr, cls);
    }

    public <T> T jsonToBean(String jsonStr, Class<T> cls, Class attrCls) {
        return JsonEntityParese.instance().jsonToBean(jsonStr, cls, attrCls);
    }

    public boolean isExites(String beanId) {
        return pService.isExites(beanId);
    }

    private void doMethodParams(Protocol protocol, Map<String, Object> params) {
        if (!RpcUtil.isEmpty(params)) {
            // 对参数的封装，对于List<Map<String,String>>类型利用protocol.setParams(String,Object)方式；对于String类型
            // 利用protocol.setParams(String,String);
            if (params != null && params.size() > 0) {
                Set<Map.Entry<String, Object>> set = params.entrySet();
                Iterator<Map.Entry<String, Object>> itr = set.iterator();
                Object obj = null;
                while (itr.hasNext()) {
                    Map.Entry<String, Object> entry = itr.next();
                    obj = entry.getValue();
                    if (obj instanceof List) {
                        protocol.setParams(entry.getKey(), obj);
                    } else if (obj instanceof String) {
                        protocol.setParams(entry.getKey(), (String) obj);
                    }
                }
            }
        }
    }

    private ResultEntity getResultEntity(String message) {
        ResultEntity resultEntity = new ResultEntity();
        resultEntity.setResult("false");
        resultEntity.setMessage(message);
        //resultEntity.setEntity("");
        return resultEntity;
    }

    private Object doService(Protocol protocol) {
        return pService.doService(protocol);
    }

    private String getSources() {
        StackTraceElement stack[] = Thread.currentThread().getStackTrace();
        if (Validator.isNull(stack)) {
            return "";
        }
        StringBuilder sb = new StringBuilder("");
        for (int i = stack.length - 1; i >= 0; i--) {
            StackTraceElement ste = stack[i];
            if (ste.getClassName().indexOf("com.apache") == -1 ||
                    (ste.getClassName() + ":" + ste.getMethodName())
                            .indexOf("LoadRpcService:getSources") != -1) {
                continue;
            }
            sb.append(ste.getClassName() + ":" + ste.getMethodName() + "->");
        }
        return sb.toString();
    }
}
